function graphChart() {
    console.log('graphChart')
    var width = 500,
        height = 500,
        fillNode = "#07a0c3",
        strokeNode = "#fff",
        fillNodeHover = "#E47F74",
        fillNeighbors = "#f0c808",
        fillText = "#333",
        strokeLink = d3.rgb(150, 150, 150, 0.2),
        strokeLinkHover = d3.rgb(50, 50, 50, 1),
        minRadius = 8,
        radius = function (node, scal) {
            return minRadius + node.weight * 3 * (scal || 1);
        };
    var dispatch;
    var miserables;

    /* ==== */
    var zoom = d3
        .zoom()
        .scaleExtent([1 / 4, 5])
        .on("zoom", zoomed);

    var svg = d3
        .select('#d3-graph')
        .attr("width", width)
        .attr("height", height)
        .call(zoom);
    var g = svg.append("g").attr("id", "force-directed-graph");
    const simulation = d3
        .forceSimulation()
        .force(
            "link",
            d3
                .forceLink()
                .id((d) => d.id)
                .distance(600)
        )
        .force(
            "charge",
            d3.forceManyBody().strength((d) => -radius(d) * 50)
        )
        .force("x", d3.forceX())
        .force("y", d3.forceY())
        .force("center", d3.forceCenter(width / 3, height / 3))
        .on("tick", function () {
            link
                .attr("x1", (d) => d.source.x)
                .attr("y1", (d) => d.source.y)
                .attr("x2", (d) => d.target.x)
                .attr("y2", (d) => d.target.y);

            node
                .selectAll('circle')
                .attr("cx", (d) => d.x)
                .attr("cy", (d) => d.y);
            node
                .selectAll("text")
                .attr("x", (d) => d.x)
                .attr("y", (d) => d.y + radius(d) / 4);

        });

    function zoomed() {
        let scale = d3.event.transform.k;
        g.attr(
            "transform",
            `translate(${d3.event.transform.x}, ${d3.event.transform.y}) scale(${scale})`
        );
    }
    const dragNode = function (simulation) {
        function dragstarted(d) {
            if (!d3.event.active) simulation.alphaTarget(0.3).restart();
            d.fx = d.x;
            d.fy = d.y;
        }

        function dragged(d) {
            d.fx = d3.event.x;
            d.fy = d3.event.y;
        }

        function dragended(d) {
            if (!d3.event.active) simulation.alphaTarget(0);
            d.fx = null;
            d.fy = null;
        }

        return d3
            .drag()
            .on("start", dragstarted)
            .on("drag", dragged)
            .on("end", dragended);
    };

    const handleMouseOver = function (d, i) {
        var neighbors = getNeighbors(this);

        d3.select(this)
            .select("circle")
            .attr("fill", 'url(#grad2)')
            .transition()
            .attr('r', (d) => radius(d, 2));

        d3.select(this).transition().select("text").attr("fill-opacity", 1);

        d3.selectAll(neighbors.nodes)
            .select("circle")
            .attr("fill", 'url(#grad3)')
            .transition()
            .attr('r', (d) => radius(d, 1.5));

        d3.selectAll(neighbors.nodes)
            .select("text")
            .transition()
            .attr("fill-opacity", 1);

        d3.selectAll(neighbors.links)
            .attr('class', 'line-path')
            .transition()
            .attr("stroke", strokeLinkHover)
            .attr("stroke-opacity", 1)
            .attr("stroke-width", 6);
    };

    const handleMouseOut = function (d, i) {
        var neighbors = getNeighbors(this);
        d3.select(this).select("circle")
            .attr("fill", 'url(#grad1)')
            .transition().attr('r', (d) => radius(d));

        d3.selectAll(".nodes text")
            .filter((d, i) => d.weight < 10)
            .transition()
            .attr("fill-opacity", 0);

        d3.selectAll(neighbors.nodes)
            .select("circle")
            .attr("fill", 'url(#grad1)')
            .transition()
            .attr('r', (d) => radius(d));

        d3.selectAll(neighbors.links)
            .attr('class', '')
            .transition()
            .attr("stroke", strokeLink)
            .attr("stroke-width", 2)
            .attr("stroke-opacity", .5)
    };

    var handleMouseClick = function (d, i) {
        const cid = d.id;
        const oldNode = new Map(node.data().map(d => [d.id, d]));
        const _links = (link.data().filter(d => (d.source.id === cid || d.target.id === cid))).map(l => ({ source: l.source.id, target: l.target.id }));
        const { nodes } = getNeighbors(this);
        const _nodeArr = nodes.split(',').map(s => s.slice(1)).concat(cid)
        const _nodes = _nodeArr.map(d => Object.assign(oldNode.get(d) || {}, d));
        if (node.data().length === _nodes.length) {
            return;
        } else {
            dispatch.call('nodeClick', this, { source: d, nodes: _nodes, links: _links })
            chart.update({ nodes: _nodes, links: _links })
        }
    }
    var getNeighbors = function (node) {
        var id = d3.select(node).attr("id");
        // Filter the links about the selected character
        var neighbors = [];
        link.each(
            (link) => {
                if (link.source.id === id || link.target.id === id) {
                    neighbors.push(link)
                }
            }
        );
        // Creates an array with all the node id that has interacted with the selected node
        // The prepend hashtag will help us to select in CSS th nodes
        const neighbors_nodes = neighbors.map(function (link) {
            if (link.source.id === id) {
                return "#" + link.target.id;
            } else {
                return "#" + link.source.id;
            }
        });

        var neighbors_edges = neighbors
            .map((d) => "#link_" + d.index)
            .join(",");

        return {
            nodes: neighbors_nodes.join(),
            links: neighbors_edges
        };
    };


    var link = g
        .append("g")
        .attr("class", "links")
        .attr("stroke", strokeLink)
        .attr("stroke-opacity", 1)
        .selectAll("line")

    var node = g
        .append("g")
        .attr("class", "nodes")
        .selectAll("g")
    const chart = Object.assign({}, {
        update({ nodes, links }) {
            console.log('update')
            console.log(nodes, links)
            svg.call(
                zoom.transform,
                d3.zoomIdentity.scale(0.6).translate(width / 1.5, height / 1.8)
            );
            // Make a shallow copy to protect against mutation, while
            // recycling old nodes to preserve position and velocity.
            const old = new Map(node.data().map(d => [d.id, d]));
            if (node.data().length > nodes.length) {
                nodes = nodes.map(d => Object.assign(old.get(d.id) || {}, d));
            } else {
            }
            links = links.map(d => Object.assign({}, d));
            node = node
                .data(nodes, d => d.id)
                .join(enter => enter.append("g")
                    .attr("class", "node")
                    .attr("id", (d) => d.id)
                    .on("mouseover", handleMouseOver)
                    .on("mouseout", handleMouseOut)
                    .on("click", handleMouseClick)
                    .call(dragNode(simulation))
                    .call(enter => enter.append("circle")
                        .attr("r", (d) => radius(d))
                        .attr("fill", 'url(#grad1)')
                        .attr("stroke", strokeNode)
                        .attr("stroke-width", 3))
                    .call(enter => enter.append("text")
                        .text((d) => d.label.toUpperCase())
                        .attr("fill", fillText)
                        .attr("fill-opacity", 1)
                        .attr("font-size", (d) => d.weight * 1)
                        .attr("text-anchor", "middle")
                        .filter((d, i) => d.weight < 8)
                        .attr("fill-opacity", 0)
                        .attr("font-size", 15)),
                    update => update,
                    exit => exit
                        .on("mouseover", null)
                        .on("mouseout", null)
                        .on("click", null)
                        .call(exit => exit.selectAll('circle').transition()
                            .duration(300)
                            .ease(d3.easeLinear)
                            .attr("r", 0)
                            .remove())
                        .call(exit => exit.selectAll('text').transition()
                            .duration(300)
                            .attr("fill-opacity", 0)
                            .remove())
                        .remove()
                )

            link = link
                .data(links)
                .join(enter => enter.append("line")
                    .attr("stroke-width", 2)
                    .attr("id", (d, i) => "link_" + i)
                    .attr("stroke-opacity", .5),
                    update => update,
                    exit => exit
                        .transition()
                        .duration(300)
                        .attr('stroke-width', 0)
                        .attr("fill-opacity", 0).remove()
                )

            simulation.nodes(nodes);
            simulation.force("link").links(links);
            simulation.alpha(1).restart();
        }
    });
    /* ==== */


    chart.width = function (value) {
        if (!arguments.length) return width;
        width = value;
        return chart;
    };

    chart.height = function (value) {
        if (!arguments.length) return height;
        height = value;
        return chart;
    };

    chart.nodes = function (value) {
        if (!arguments.length) return nodes;
        nodes = value;
        return chart;
    };

    chart.edges = function (value) {
        if (!arguments.length) return edges;
        edges = value;
        return chart;
    };
    chart.miserables = function (value) {
        if (!arguments.length) return miserables;
        miserables = value;
        return chart;
    };

    chart.fillNode = function (value) {
        if (!arguments.length) return fillNode;
        fillNode = value;
        return chart;
    };

    chart.fillNodeHover = function (value) {
        if (!arguments.length) return fillNodeHover;
        fillNodeHover = value;
        return chart;
    };

    chart.fillNeighbors = function (value) {
        if (!arguments.length) return fillNeighbors;
        fillNeighbors = value;
        return chart;
    };

    chart.radius = function (value) {
        if (!arguments.length) return radius;
        radius = value;
        return chart;
    };

    chart.fillText = function (value) {
        if (!arguments.length) return fillText;
        fillText = value;
        return chart;
    };

    chart.strokeNode = function (value) {
        if (!arguments.length) return strokeNode;
        strokeNode = value;
        return chart;
    };

    chart.strokeLinkHover = function (value) {
        if (!arguments.length) return strokeLinkHover;
        strokeLinkHover = value;
        return chart;
    };

    chart.strokeLink = function (value) {
        if (!arguments.length) return strokeLink;
        strokeLink = value;
        return chart;
    };
    chart.dispatch = function (value) {
        console.log(value)
        if (!arguments.length) return dispatch;
        dispatch = value;
        return chart;
    }
    console.dir(chart)
    return chart;
}
